/*
 * Created by JFormDesigner on Tue Oct 21 21:37:45 CST 2025
 */

package com.yeban.bams.client;

import com.yeban.bams.client.clientsocket.ClientRequest;
import com.yeban.bams.client.clientsocket.RequestHelper;
import com.yeban.bams.entity.*;
import com.yeban.bams.server.serversocket.RequestTO;
import com.yeban.bams.server.serversocket.ResponseTO;

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;

/**
 * @author yeban
 */
public class BusinessPanel extends JPanel {
    private ATMClient atm;

    // 自动刷新定时器
    private Timer autoRefreshTimer;
    private Double lastBalance;
    private long lastActionTime = 0; // 最近一次主动操作时间戳
    private volatile boolean refreshing = false;
    private volatile boolean isLocalOperation = false; // 标记是否为本账户操作（存款、取款等）
    private long localOperationTime = 0; // 本账户操作的时间戳
    private String lastSelectedBusiness = null; // 保存用户上次选择的业务类型

    public BusinessPanel(ATMClient atm) {
        this.atm = atm;
        initComponents();
    }

    // 批量向下拉框添加选项
    private void addBusinessItems(String... items) {
        for (String item : items) {
            businessComboBox.addItem(item);
        }
    }

    // 初始化或刷新业务界面（基于当前acc）
    public void init() {
        // 保存当前选择的业务（如果下拉框有选项且已选择）
        if (businessComboBox.getItemCount() > 0 && businessComboBox.getSelectedItem() != null) {
            lastSelectedBusiness = businessComboBox.getSelectedItem().toString();
        }

        // 初始化标签
        businessComboBox.removeAllItems();
        if (atm.acc == null) {
            businessComboBox.addItem("无账户");
            lastSelectedBusiness = null;
            return;
        }

        // 更新账户基础信息
        accountDataLabel.setText(String.valueOf(atm.acc.getId()));
        usernameDataLabel.setText(atm.acc.getName());
        balanceDataLabel.setText(String.format("%.2f", atm.acc.getBalance()));

        // 信用额度
        if (atm.acc instanceof CreditAccount) {
            CreditAccount credit = (CreditAccount) atm.acc;
            ceilingDataLabel.setText(String.format("%.2f", credit.getCeiling()));
        } else {
            ceilingDataLabel.setText("--");
        }

        // 贷款额度
        if (atm.acc instanceof LoanableSavingsAccount) {
            LoanableSavingsAccount loanAcc = (LoanableSavingsAccount) atm.acc;
            loanDataLabel.setText(String.valueOf(loanAcc.getLoanAmount()));
        } else if (atm.acc instanceof LoanableCreditAccount) {
            LoanableCreditAccount loanCredit = (LoanableCreditAccount) atm.acc;
            loanDataLabel.setText(String.valueOf(loanCredit.getLoanAmount()));
        } else {
            loanDataLabel.setText("--");
        }

        // 业务选项
        if (atm.acc instanceof LoanableCreditAccount) {
            addBusinessItems("存款", "取款", "转账", "设置透支额度", "贷款", "还贷");
        } else if (atm.acc instanceof LoanableSavingsAccount) {
            addBusinessItems("存款", "取款", "转账", "贷款", "还贷");
        } else if (atm.acc instanceof CreditAccount) {
            addBusinessItems("存款", "取款", "转账", "设置透支额度");
        } else if (atm.acc instanceof SavingAccount) {
            addBusinessItems("存款", "取款", "转账");
        }

        // 恢复之前选择的业务（如果该业务在当前账户类型中仍然可用）
        if (lastSelectedBusiness != null) {
            for (int i = 0; i < businessComboBox.getItemCount(); i++) {
                if (businessComboBox.getItemAt(i).equals(lastSelectedBusiness)) {
                    businessComboBox.setSelectedIndex(i);
                    break;
                }
            }
        }
    }

    // 刷新数据（从服务器请求最新账户信息）+调用init
    public void refreshPanel() {
        if (atm.acc == null) {
            JOptionPane.showMessageDialog(this, "当前无有效账户，无法刷新！", "刷新失败", JOptionPane.WARNING_MESSAGE);
            clear();
            return;
        }
        if (atm.acc.getId() == null) {
            System.out.println("错误：账户ID为null，无法刷新！");
            JOptionPane.showMessageDialog(this, "账户ID无效，无法刷新！", "刷新失败", JOptionPane.ERROR_MESSAGE);
            return;
        }
        if (refreshing)
            return;
        refreshing = true;
        System.out.println("开始刷新 - 账户ID: " + atm.acc.getId());
        RequestTO reqRefresh = new RequestTO(
                "getAccountById",
                new Object[] { atm.acc.getId() },
                new Class[] { Long.class });
        new SwingWorker<ResponseTO, Void>() {
            @Override
            protected ResponseTO doInBackground() {
                return ClientRequest.send(reqRefresh);
            }

            @Override
            protected void done() {
                try {
                    ResponseTO respRefresh = get();
                    if (respRefresh.isSuccess()) {
                        atm.acc = (Account) respRefresh.getData();
                        init();
                    } else {
                        JOptionPane.showMessageDialog(BusinessPanel.this, "刷新失败：" + respRefresh.getMessage(), "错误",
                                JOptionPane.ERROR_MESSAGE);
                    }
                } catch (Exception ex) {
                    JOptionPane.showMessageDialog(BusinessPanel.this, "刷新失败：" + ex.getMessage(), "错误",
                            JOptionPane.ERROR_MESSAGE);
                } finally {
                    refreshing = false;
                }
            }
        }.execute();
    }

    // 清空页面
    public void clear() {
        accountDataLabel.setText("");
        usernameDataLabel.setText("");
        balanceDataLabel.setText("");
        ceilingDataLabel.setText("");
        loanDataLabel.setText("");
        businessComboBox.removeAllItems();
        moneyTextField.setText("");
        lastSelectedBusiness = null; // 清空时重置选择
    }

    // 静默刷新
    private void refreshPanelSilent() {
        if (atm.acc == null) {
            clear();
            return;
        }
        if (refreshing)
            return;
        refreshing = true;
        double oldBalance = atm.acc.getBalance();
        RequestTO request = new RequestTO(
                "getAccountById",
                new Object[] { atm.acc.getId() },
                new Class[] { Long.class });
        new SwingWorker<ResponseTO, Void>() {
            @Override
            protected ResponseTO doInBackground() {
                return ClientRequest.send(request);
            }

            @Override
            protected void done() {
                try {
                    ResponseTO response = get();
                    if (response.isSuccess()) {
                        atm.acc = (Account) response.getData();
                        double newBalance = atm.acc.getBalance();
                        if (newBalance > oldBalance) {
                            long now = System.currentTimeMillis();
                            long interval = now - lastActionTime;
                            // 只有在以下情况才弹框提示转账：
                            // 1. 距离上次操作时间超过3秒（避免本账户操作后立即刷新误判）
                            // 2. 不是本账户操作导致的余额增加（通过时间窗口判断：5秒内认为是本账户操作）
                            long localOpInterval = now - localOperationTime;
                            if (interval > 3000 && localOpInterval > 5000) {
                                double money = newBalance - oldBalance;
                                JOptionPane.showMessageDialog(BusinessPanel.this,
                                        "账户xxx向您转账" + String.format("%.2f", money) + "元\n当前余额"
                                                + String.format("%.2f", newBalance),
                                        "转账收款",
                                        JOptionPane.INFORMATION_MESSAGE);
                            }
                            init();
                        } else {
                            // 余额没有增加，直接刷新
                            init();
                        }
                    }
                } catch (Exception ex) {
                } finally {
                    refreshing = false;
                }
            }
        }.execute();
    }

    // 开启自动刷新
    public void startAutoRefresh() {
        // 先考虑非正常情况：如果已经开启，无需再开启
        if (autoRefreshTimer != null)
            return;
        // 设置刷新间隔为5秒，避免刷新太频繁影响用户操作
        autoRefreshTimer = new Timer(5000, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                refreshPanelSilent();
            }
        });
        autoRefreshTimer.setRepeats(true); // 定时器重复触发
        autoRefreshTimer.start(); // 启动定时器
    }

    // 关闭自动刷新
    public void stopAutoRefresh() {
        // 服务停止 + 标识符更新
        if (autoRefreshTimer != null)
            autoRefreshTimer.stop();
        autoRefreshTimer = null;
    }

    // 【返回】跳转至主页面
    private void back(ActionEvent e) {
        // 显式登出（如果当前有登录账户）
        if (atm.acc != null) {
            RequestTO reqLogout = new RequestTO(
                    "logout",
                    new Object[] { atm.acc.getId() },
                    new Class[] { Long.class });
            ClientRequest.send(reqLogout);
            atm.acc = null;
        }
        CardLayout cardLayout = (CardLayout) atm.getContentPane().getLayout();
        cardLayout.show(atm.getContentPane(), "mainPanel");
        atm.businessPanel.stopAutoRefresh();
    }

    // 【提交】跳转至业务页面
    private void submit(ActionEvent e) {
        // 如果账户为空
        if (atm.acc == null) {
            JOptionPane.showMessageDialog(this, "当前无有效账户！", "错误", JOptionPane.ERROR_MESSAGE);
            return;
        }
        // 收集用户操作数据
        String selected = businessComboBox.getSelectedItem().toString();
        // 初始化输入的金额
        double money = 0.0;
        // 用户操作
        try {
            money = Double.parseDouble(moneyTextField.getText().trim()); // money: 接收操作的金额
        } catch (NumberFormatException ex) {
            JOptionPane.showMessageDialog(this, "请输入正确的金额格式！", "输入错误", JOptionPane.WARNING_MESSAGE);
            return;
        }
        // 业务操作
        try {
            switch (selected) {
                case "存款":
                    // 构造请求对象
                    RequestTO reqDeposit = new RequestTO("deposit",
                            new Object[] { atm.acc.getId(), money },
                            new Class[] { Long.class, double.class });
                    RequestHelper.sendRequestAsync(this, reqDeposit, "存款", respDeposit -> {
                        if (respDeposit.isSuccess()) {
                            lastActionTime = System.currentTimeMillis();
                            localOperationTime = System.currentTimeMillis(); // 标记为本账户操作
                            isLocalOperation = true;
                            refreshPanel();
                        }
                    });
                    break;
                case "取款":
                    if (!promptPassword())
                        return; // 密码验证
                    RequestTO reqWithdraw = new RequestTO(
                            "withdraw",
                            new Object[] { atm.acc.getId(), atm.acc.getPassword(), money },
                            new Class[] { Long.class, String.class, double.class });
                    RequestHelper.sendRequestAsync(this, reqWithdraw, "取款", respWithdraw -> {
                        if (respWithdraw.isSuccess()) {
                            lastActionTime = System.currentTimeMillis();
                            localOperationTime = System.currentTimeMillis(); // 标记为本账户操作
                            isLocalOperation = true;
                            refreshPanel();
                        }
                    });
                    break;
                case "设置透支额度":
                    if (!promptPassword())
                        return; // 密码验证
                    RequestTO reqUpdateCeiling = new RequestTO(
                            "updateCeiling",
                            new Object[] { atm.acc.getId(), atm.acc.getPassword(), money },
                            new Class[] { Long.class, String.class, double.class });
                    RequestHelper.sendRequestAsync(this, reqUpdateCeiling, "设置透支额度", respUpdateCeiling -> {
                        if (respUpdateCeiling.isSuccess()) {
                            lastActionTime = System.currentTimeMillis();
                            localOperationTime = System.currentTimeMillis(); // 标记为本账户操作
                            isLocalOperation = true;
                            refreshPanel();
                        }
                    });
                    break;
                case "贷款":
                    RequestTO reqRequestLoan = new RequestTO(
                            "requestLoan",
                            new Object[] { atm.acc.getId(), money },
                            new Class[] { Long.class, double.class });
                    RequestHelper.sendRequestAsync(this, reqRequestLoan, "贷款", respRequestLoan -> {
                        if (respRequestLoan.isSuccess()) {
                            lastActionTime = System.currentTimeMillis();
                            localOperationTime = System.currentTimeMillis(); // 标记为本账户操作
                            isLocalOperation = true;
                            refreshPanel();
                        }
                    });
                    break;
                case "还贷":
                    RequestTO reqPayLoan = new RequestTO(
                            "payLoan",
                            new Object[] { atm.acc.getId(), money },
                            new Class[] { Long.class, double.class });
                    RequestHelper.sendRequestAsync(this, reqPayLoan, "还贷", respPayLoan -> {
                        if (respPayLoan.isSuccess()) {
                            lastActionTime = System.currentTimeMillis();
                            localOperationTime = System.currentTimeMillis(); // 标记为本账户操作
                            isLocalOperation = true;
                            refreshPanel();
                        }
                    });
                    break;
                case "转账":
                    handleTransferAsync(money);
                    break;
                default:
                    JOptionPane.showMessageDialog(this, "未知操作类型！", "操作失败", JOptionPane.ERROR_MESSAGE);
            }
        } catch (Exception ex) {
            JOptionPane.showMessageDialog(this, ex.getMessage(), "操作失败", JOptionPane.ERROR_MESSAGE);
        }
    }

    // 弹出密码框，让用户输入密码，并验证
    private boolean promptPassword() {
        int count = 0;
        while (count < 5) {
            // 弹出密码框
            JPasswordField pwdField = new JPasswordField();
            int option = JOptionPane.showConfirmDialog(this, pwdField, "请输入密码确认操作", JOptionPane.OK_CANCEL_OPTION,
                    JOptionPane.PLAIN_MESSAGE);
            // 用户点击取消
            if (option != JOptionPane.OK_OPTION) {
                return false;
            }
            // 获取输入的密码
            String inputPwd = new String(pwdField.getPassword()).trim();
            if (inputPwd.isEmpty()) {
                JOptionPane.showMessageDialog(this, "密码不能为空", "输入错误", JOptionPane.WARNING_MESSAGE);
            }
            // 验证密码是否正确
            if (inputPwd.equals(atm.acc.getPassword())) {
                return true;
            } else { // 密码错误
                count++;
                if (count < 5) {
                    JOptionPane.showMessageDialog(this, "密码错误！", "验证失败", JOptionPane.ERROR_MESSAGE);
                } else {
                    JOptionPane.showMessageDialog(this, "输入密码次数过多！账户锁定！", "操作失败", JOptionPane.INFORMATION_MESSAGE);
                }
            }
        }
        return false;
    }

    // 转账操作
    private void handleTransferAsync(double money) {
        // 1.输入要转入账户的ID
        String toIdStr = JOptionPane.showInputDialog(this, "请输入转入账户的ID：", "转账账户", JOptionPane.PLAIN_MESSAGE);
        if (toIdStr == null || toIdStr.trim().isEmpty()) {
            JOptionPane.showMessageDialog(this, "已取消转账或输入账户ID为空", "操作取消", JOptionPane.INFORMATION_MESSAGE);
            return;
        }

        // 2.检测账户是否存在
        long toId;
        try {
            toId = Long.parseLong(toIdStr);
        } catch (NumberFormatException ex) {
            JOptionPane.showMessageDialog(this, "转入账户ID格式不正确！", "输入错误", JOptionPane.ERROR_MESSAGE);
            return;
        }

        // 2.1转账前先检查是否是自己
        if (String.valueOf(atm.acc.getId()).equals(toIdStr.trim())) {
            JOptionPane.showMessageDialog(this,
                    "不能向自己转账！",
                    "转账失败", JOptionPane.WARNING_MESSAGE);
            return;
        }

        // 3.确认是否继续转账
        Account acc = null;
        int confirm = JOptionPane.showConfirmDialog(this, "是否要向账户" + toIdStr + "转账" + money + "元", "确认转账",
                JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE);
        if (confirm != JOptionPane.OK_OPTION) {
            JOptionPane.showMessageDialog(this, "已取消转账操作。", "操作取消", JOptionPane.INFORMATION_MESSAGE);
            return;
        }

        // 4.密码验证
        if (!promptPassword())
            return;

        // 5.转账逻辑
        RequestTO reqTransfer = new RequestTO(
                "transfer",
                new Object[] { atm.acc.getId(), atm.acc.getPassword(), Long.parseLong(toIdStr), money },
                new Class[] { Long.class, String.class, Long.class, double.class });
        RequestHelper.sendRequestAsync(this, reqTransfer, "转账", respTransfer -> {
            if (respTransfer.isSuccess()) {
                lastActionTime = System.currentTimeMillis();
                // 转账操作不设置localOperationTime，这样接收方在自动刷新时能正确识别为转账
                isLocalOperation = false;
                refreshPanel();
            }
        });
    }

    private void initComponents() {
        // JFormDesigner - Component initialization - DO NOT MODIFY  //GEN-BEGIN:initComponents  @formatter:off
        accountLabel = new JLabel();
        usernameLabel = new JLabel();
        balanceLabel = new JLabel();
        ceilingLabel = new JLabel();
        loanLabel = new JLabel();
        moneyTextField = new JTextField();
        submitButton = new JButton();
        accountDataLabel = new JLabel();
        usernameDataLabel = new JLabel();
        balanceDataLabel = new JLabel();
        ceilingDataLabel = new JLabel();
        loanDataLabel = new JLabel();
        businessComboBox = new JComboBox<>();
        businessTitleLabel = new JLabel();
        backButton = new JButton();

        //======== this ========
        setForeground(Color.black);
        setLayout(null);

        //---- accountLabel ----
        accountLabel.setText("\u8d26     \u6237");
        accountLabel.setFont(new Font("Microsoft YaHei UI", Font.PLAIN, 16));
        accountLabel.setHorizontalAlignment(SwingConstants.CENTER);
        add(accountLabel);
        accountLabel.setBounds(105, 160, 115, 40);

        //---- usernameLabel ----
        usernameLabel.setText("\u59d3     \u540d");
        usernameLabel.setFont(new Font("Microsoft YaHei UI", Font.PLAIN, 16));
        usernameLabel.setHorizontalAlignment(SwingConstants.CENTER);
        add(usernameLabel);
        usernameLabel.setBounds(105, 210, 115, 40);

        //---- balanceLabel ----
        balanceLabel.setText("\u4f59     \u989d");
        balanceLabel.setFont(new Font("Microsoft YaHei UI", Font.PLAIN, 16));
        balanceLabel.setHorizontalAlignment(SwingConstants.CENTER);
        add(balanceLabel);
        balanceLabel.setBounds(105, 260, 115, 40);

        //---- ceilingLabel ----
        ceilingLabel.setText("\u4fe1 \u7528 \u989d \u5ea6");
        ceilingLabel.setFont(new Font("Microsoft YaHei UI", Font.PLAIN, 16));
        ceilingLabel.setHorizontalAlignment(SwingConstants.CENTER);
        add(ceilingLabel);
        ceilingLabel.setBounds(105, 310, 115, 40);

        //---- loanLabel ----
        loanLabel.setText("\u8d37 \u6b3e \u989d");
        loanLabel.setFont(new Font("Microsoft YaHei UI", Font.PLAIN, 16));
        loanLabel.setHorizontalAlignment(SwingConstants.CENTER);
        add(loanLabel);
        loanLabel.setBounds(105, 360, 115, 40);
        add(moneyTextField);
        moneyTextField.setBounds(260, 420, 130, 42);

        //---- submitButton ----
        submitButton.setText("\u63d0\u4ea4");
        submitButton.setFont(new Font("Microsoft YaHei UI", Font.BOLD, 14));
        submitButton.addActionListener(e -> submit(e));
        add(submitButton);
        submitButton.setBounds(105, 470, 120, 45);

        //---- accountDataLabel ----
        accountDataLabel.setText("         account");
        accountDataLabel.setHorizontalAlignment(SwingConstants.LEFT);
        accountDataLabel.setFont(new Font("Microsoft YaHei UI", Font.PLAIN, 16));
        add(accountDataLabel);
        accountDataLabel.setBounds(255, 160, 190, 40);

        //---- usernameDataLabel ----
        usernameDataLabel.setText("username");
        usernameDataLabel.setHorizontalAlignment(SwingConstants.CENTER);
        usernameDataLabel.setFont(new Font("Microsoft YaHei UI", Font.PLAIN, 16));
        add(usernameDataLabel);
        usernameDataLabel.setBounds(255, 210, 150, 40);

        //---- balanceDataLabel ----
        balanceDataLabel.setText("balance");
        balanceDataLabel.setHorizontalAlignment(SwingConstants.CENTER);
        balanceDataLabel.setFont(new Font("Microsoft YaHei UI", Font.PLAIN, 16));
        add(balanceDataLabel);
        balanceDataLabel.setBounds(255, 260, 150, 40);

        //---- ceilingDataLabel ----
        ceilingDataLabel.setText("ceiling");
        ceilingDataLabel.setHorizontalAlignment(SwingConstants.CENTER);
        ceilingDataLabel.setFont(new Font("Microsoft YaHei UI", Font.PLAIN, 16));
        add(ceilingDataLabel);
        ceilingDataLabel.setBounds(255, 310, 150, 40);

        //---- loanDataLabel ----
        loanDataLabel.setText("loan");
        loanDataLabel.setHorizontalAlignment(SwingConstants.CENTER);
        loanDataLabel.setFont(new Font("Microsoft YaHei UI", Font.PLAIN, 16));
        add(loanDataLabel);
        loanDataLabel.setBounds(255, 360, 150, 40);

        //---- businessComboBox ----
        businessComboBox.setModel(new DefaultComboBoxModel<>(new String[] {
            "\u5b58\u6b3e",
            "\u53d6\u6b3e",
            "\u8f6c\u8d26"
        }));
        businessComboBox.setFont(new Font("Microsoft YaHei UI", Font.BOLD, 14));
        add(businessComboBox);
        businessComboBox.setBounds(100, 420, 130, 40);

        //---- businessTitleLabel ----
        businessTitleLabel.setText("\u4e1a   \u52a1   \u754c   \u9762");
        businessTitleLabel.setHorizontalAlignment(SwingConstants.CENTER);
        businessTitleLabel.setFont(new Font("Microsoft YaHei UI", Font.BOLD, 30));
        add(businessTitleLabel);
        businessTitleLabel.setBounds(108, 70, 275, 65);

        //---- backButton ----
        backButton.setText("\u8fd4\u56de");
        backButton.setFont(new Font("Microsoft YaHei UI", Font.BOLD, 14));
        backButton.addActionListener(e -> back(e));
        add(backButton);
        backButton.setBounds(265, 470, 120, 45);

        setPreferredSize(new Dimension(500, 650));
        // JFormDesigner - End of component initialization  //GEN-END:initComponents  @formatter:on
    }

    // JFormDesigner - Variables declaration - DO NOT MODIFY  //GEN-BEGIN:variables  @formatter:off
    private JLabel accountLabel;
    private JLabel usernameLabel;
    private JLabel balanceLabel;
    private JLabel ceilingLabel;
    private JLabel loanLabel;
    private JTextField moneyTextField;
    private JButton submitButton;
    private JLabel accountDataLabel;
    private JLabel usernameDataLabel;
    private JLabel balanceDataLabel;
    private JLabel ceilingDataLabel;
    private JLabel loanDataLabel;
    private JComboBox<String> businessComboBox;
    private JLabel businessTitleLabel;
    private JButton backButton;
    // JFormDesigner - End of variables declaration  //GEN-END:variables  @formatter:on
}
